package com.ct.config.thread;

import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.lang.reflect.Method;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * spring管理的定义线程池，可以在方法上搭配@Async("name")注解使用
 *
 *
 * AsyncConfigurer接口位于org.springframework.scheduling.annotation包中，用于配置异步执行相关的配置。
 * AsyncConfigurer接口定义了两个默认方法：
 *   getAsyncExecutor()方法用于获取异步方法执行的执行器（Executor）。通过实现该方法，可以自定义异步方法执行的线程池、任务调度器等执行器。
 *   getAsyncUncaughtExceptionHandler()方法用于获取异步方法执行的未捕获异常处理器（AsyncUncaughtExceptionHandler）。
 *   通过实现该方法，可以自定义处理异步方法执行过程中发生的未捕获异常的逻辑。
 *
 * 通过实现AsyncConfigurer接口，并重写这两个方法，可以对整个应用程序中使用的异步方法执行进行全局配置。
 * 例如，您可以配置一个共享的线程池，或者指定一个统一的未捕获异常处理逻辑。
 * 使用AsyncConfigurer接口可以更方便地进行异步方法的配置和管理，而不需要在每个异步方法上都进行独立的配置。
 */
@Slf4j
@Configuration
public class SpringThreadPoolConfig implements AsyncConfigurer {

    public final static String SIMPLE_TASK_EXECUTOR_NAME  = "simpleExecutor";

    /**
     * 我们可以把这个线程池定义为简单任务的线程池。
     * 即:这个线程池可以用来执行代码中一些业务场景不是很复杂的异步任务。
     */
    @Bean(SIMPLE_TASK_EXECUTOR_NAME)
    @Override
    public ThreadPoolTaskExecutor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10); // 线程池核心线程数量 (默认值1,因此导致线程不能被重用的最主要原因) 同时允许n个客户端同时操作这个线程池
        executor.setMaxPoolSize(20); // 线程池维护的线程的最大数量，当核心线程都被用完且缓冲队列都满了之后才会申请超过核心线程数量的线程
        executor.setQueueCapacity(1000); // 缓冲队列任务个数
        executor.setKeepAliveSeconds(60); // 超出核心线程数之外的空闲最大存活时间，超出该时间会被杀掉
        executor.setThreadNamePrefix("simple_async_"); // 线程名前缀

        executor.setWaitForTasksToCompleteOnShutdown(true); // 是否等待所有线程执行完毕后才关闭线程池。默认false，如果关掉程序，会导致未执行完的线程也退出，从而可能出BUG
        executor.setAwaitTerminationSeconds(60); // 等待的时常，默认0(不等待)，程序shutdown之后等待的时间，跟上面的配置一起使用

        // 拒绝策略
        executor.setRejectedExecutionHandler(
                // AbortPolicy: 如果线程池运行的任务满了，而且队列满了，直接抛出异常，abort(废弃)掉
                // DiscardOldestPolicy: 当线程池的数量等于最大线程数,而且队列满，抛弃线程池中最后一个要执行的任务，并执行新传递进来的任务
                // DiscardPolicy: 当线程池的数量等于最大线程数, 不做任何动作，直接丢弃
                new ThreadPoolExecutor.AbortPolicy()
        );

        executor.initialize();
        return executor;
    }

    /**
     * 定义异步任务异常处理类
     */
    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new AsyncExceptionHandler();
    }

    class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {

        /**
         * @param throwable 异步处理任务抛出的异常对象
         * @param method    异常处理任务方法
         * @param objects   数组，异常处理服务需要传递进来的参数
         */
        @Override
        public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
            log.info("AsyncError: {}, Method: {}, Param: {}",
                    throwable.getMessage(),
                    method.getName(),
                    objects);
            log.error("error:" + throwable);
            // 一些逻辑 例如 发送邮件或者短信
        }
    }
}